#include "LexicalAnalyze.h"
#include <iostream>

LexicalAnalyze::LexicalAnalyze()
{
	clear();
}

void LexicalAnalyze::clear()
{
	m_result.clear();
	m_expect.clear();
	m_cstr.reset();
}

bool LexicalAnalyze::is_integrity()
{
	return m_expect.empty();
}

std::string LexicalAnalyze::get_expect()
{
	return m_expect;
}

bool LexicalAnalyze::try_saveid(std::string& ctx, int line, int cols)
{
	ctx = m_cstr.trim(ctx);
	if(ctx != "" && (!ctx.empty()))
	{
		m_result.push_back(LexElement(LET_Identifier, ctx, line, cols));
		ctx = "";
		return true;
	}

	return false;
}

bool LexicalAnalyze::analyze(const std::string& src)
{
	m_cstr.add_source(src);

	std::string ctx;
	int line = 0;
	int cols = 0;

	struct LexicalItem
	{
		LexElementType type;
		char start, end;
	};

	LexicalItem lexitems[] = 
	{
		{ LET_EscapeString, '\"', '\"' },
		{ LET_DirectString, '\'', '\'' },
		{ LET_Parentheses,  '(',  ')'  },
		{ LET_Brackets,     '[',  ']'  },
		{ LET_Braces,       '{',  '}'  }
	};

	const char oneOperItems[] =
	{
		'+', '-', '*', '/', '%', '^',
		'!', '&', '|', '>', '<', '=',
		'@', '$', '?', ':', ';', ',',
		'.', '~', '`'
	};

	const char* twoOperItems [] = 
	{
		"++", "--", "&&", "||",
		">=", "<=", "==", "!=",
		":=", "+=", "-=", "*=", "/=", "%=", "^=",
		"->", "**", ">>", "<<", "<>", "<-", "--",
		"::", "=>", "<=", "~~", "~=", "..", ".=",
		"&=", "|=", "@=", "$=", "``", "`=", ",="
	};

	while(!m_cstr.is_end())
	{
		line = m_cstr.get_curline();
		cols = m_cstr.get_curcols();

		if(m_cstr.is_start_with('#', '!'))
		{
			try_saveid(ctx, line, cols);

			if(m_cstr.get_text('\n', 2, ctx, true, true))
			{
				m_result.push_back(LexElement(LET_Directive, ctx, line, cols));
				ctx = "";
			}
		}
		else if(m_cstr.is_start_with('#'))
		{
			try_saveid(ctx, line, cols);
			m_cstr.get_text('\n', 1, ctx, true, true);
			ctx = "";
		} 
		else if(m_cstr.is_start_with('/', '/'))
		{
			try_saveid(ctx, line, cols);
			m_cstr.get_text('\n', 2, ctx, true, true);
			ctx = "";
		}
		else if(m_cstr.is_start_with('/', '*'))
		{
			try_saveid(ctx, line, cols);
			if(m_cstr.get_text('*', '/', ctx))
			{
				ctx = "";
			}
			else
			{
				m_expect = "*/";
				return false;
			}
		}
		else if(m_cstr.is_start_with('\"', '\"', '\"'))
		{
			try_saveid(ctx, line, cols);

			if(m_cstr.get_text('\"', '\"', '\"', 3, ctx))
			{
				m_result.push_back(LexElement(LET_LongString, ctx, line, cols));
				ctx = "";
			}
			else
			{
				m_expect  = "\"\"\"";
				return false;
			}
		}
		else
		{
			for(size_t i = 0; i < sizeof(lexitems) / sizeof(lexitems[0]); ++i)
			{
				if(m_cstr.is_start_with(lexitems[i].start))
				{
					try_saveid(ctx, line, cols);

					if(m_cstr.get_text(lexitems[i].end, 1, ctx))
					{
						m_result.push_back(LexElement(lexitems[i].type, ctx, line, cols));
						ctx = "";
						goto while_end;
					}
					else
					{
						m_expect = lexitems[i].end;
						return false;
					}
				}
			} // for

			for(size_t i = 0; i < sizeof(twoOperItems) / sizeof(twoOperItems[0]); ++i)
			{
				if(m_cstr.is_start_with(twoOperItems[i][0], twoOperItems[i][1]))
				{
					try_saveid(ctx, line, cols);

					m_result.push_back(LexElement(LET_Operator, twoOperItems[i], line, cols));
					ctx = "";
					m_cstr.skip(2);

					goto while_end;
				}
			}

			for(size_t i = 0; i < sizeof(oneOperItems) / sizeof(oneOperItems[0]); ++i)
			{
				if(m_cstr.is_start_with(oneOperItems[i]))
				{
					try_saveid(ctx, line, cols);

					ctx += oneOperItems[i];
					m_result.push_back(LexElement(LET_Operator, ctx, line, cols));
					ctx = "";
					m_cstr.skip(1);

					goto while_end;
				}
			}

			char c = m_cstr.get();
			if(c == '\n')
			{
				try_saveid(ctx, line, cols);
			}
			else
			{
				ctx += c;
			}
		}

while_end:
		;
	}

	try_saveid(ctx, line, cols);

	return true;
}

void LexicalAnalyze::print_result()
{
	for(size_t i = 0; i < m_result.size(); ++i)
	{
		std::cout << m_result[i].m_type << " "
			<< m_result[i].m_line << "-" 
			<< m_result[i].m_cols << "\t"
			<< m_result[i].m_context << std::endl;
	}
}